package cn.com.liuxiaoluxx.cloud.api.exception;

import com.google.common.base.Joiner;
import com.google.common.collect.Lists;

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;

import cn.com.liuxiaoluxx.cloud.api.result.RestResult;
import cn.com.liuxiaoluxx.cloud.facade.exception.CloudException;

/**
 * The type ExceptionHandler.
 * <p>
 *
 * @author Xiaolu.Liu
 * @date 2019/6/27
 */
@ControllerAdvice
@Component
public class GlobalExceptionHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResult handle(MethodArgumentNotValidException exception, HttpServletRequest request) {
        BindingResult bindingResult = exception.getBindingResult();

        return handleBindResult(bindingResult);
    }

    @ExceptionHandler(BindException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public RestResult handle(BindException exception, HttpServletRequest request) {
        BindingResult bindingResult = exception.getBindingResult();

        return handleBindResult(bindingResult);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public RestResult handle(ConstraintViolationException exception, HttpServletRequest request) {

        Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
        Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
        List<String> msgList = Lists.newArrayList();
        while (iterator.hasNext()) {
            ConstraintViolation<?> cvl = iterator.next();
            String[] str = cvl.getPropertyPath().toString().split("\\.");
            if (str.length > 0) {
                msgList.add(str[str.length - 1] + "：" + cvl.getMessage());
            }
        }
        String msg = Joiner.on(",").join(msgList);
        return RestResult.failure(400, msg);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ResponseBody
    public RestResult handle(MissingServletRequestParameterException exception, HttpServletRequest request) {
        return RestResult.failure(400, exception.getMessage());
    }

    @ExceptionHandler(CloudException.class)
    @ResponseBody
    public RestResult handle(CloudException exception, HttpServletRequest request) {
        return RestResult.failure(400, exception.getMessage());
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public RestResult handle(Exception exception, HttpServletRequest request) {
        return RestResult.failure(400, exception.getMessage());
    }

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    public RestResult handle(Throwable exception, HttpServletRequest request) {
        return RestResult.failure(400, exception.getMessage());
    }

    private RestResult handleBindResult(BindingResult bindingResult) {
        if (bindingResult != null && bindingResult.hasErrors()) {
            String errorMessage = bindingResult.getAllErrors().stream()
                                               .map(error -> ((FieldError) error).getField() + " "+ error.getDefaultMessage())
                                               .collect(Collectors.joining(","));

            return RestResult.failure(400, errorMessage);
        }

        return RestResult.failure(400, "参数校验不通过");
    }
}
